#include <stdlib.h>

#include <libxml/tree.h>
#include <xmlsec/xmltree.h>
#include <glib.h>

#include "../mcx/mcx.h"
#include "charon.h"

static xmlDocPtr cfg;

const char ns_config[] = "urn:fish";
const char ns_charon[] = "urn:frog";

static GSList *configs;

static void iterate(GHashTable *h, xmlNodePtr p, gpointer value) {
  xmlNodePtr cur;
  for(cur = p->xmlChildrenNode; cur; cur = cur->next) {
    g_hash_table_insert(h, (gpointer)xmlSecNodeGetName(cur), value);
  }
}

struct config *ch_config_parse(xmlNodePtr c) {
  struct config *ret;
  xmlNodePtr ptr;

  if(!xmlSecCheckNodeName(c, "config", ns_config))
    return NULL;
  ret = calloc(1, sizeof(*ret));
  ret->map = g_hash_table_new(g_str_hash, g_str_equal);
  if((ptr = xmlSecFindChild(c, "KeyInfo", xmlSecDSigNs)))
    if(!(ret->root = ch_key_extract(ptr, NULL)))
      goto fail;
  if((ptr = xmlSecFindChild(c, "permit", ns_config)))
    iterate(ret->map, ptr, (gpointer)2);
  if((ptr = xmlSecFindChild(c, "verify", ns_config)))
    iterate(ret->map, ptr, (gpointer)1);
  if((ptr = xmlSecFindChild(c, "condition", ns_charon)))
    ret->condition = ptr;
  return ret;
 fail:
  // XXX - free everything
  return NULL;
}

int ch_config_load(char *path) {
  xmlNodePtr root, cp;
  
  if(!path)
    path = SYSCONFDIR "/config.xml";
  
  cfg = validateXmlFile(DATADIR "/config.xsd", SYSCONFDIR "/config.xml");
  if(!cfg || !(root = xmlDocGetRootElement(cfg)) ||
     !xmlSecCheckNodeName(root, "configList", ns_config))
    return -1;
  for(cp = root->xmlChildrenNode; cp; cp = cp->next) {
    struct config *c;
    
    if(!xmlSecCheckNodeName(cp, "config", ns_config))
      continue;
    if((c = ch_config_parse(cp)))
      configs = g_slist_append(configs, c);
     
  }
  return !configs;
}

static void match_root(gpointer cp, gpointer rp) {
  struct config *cfg = cp;
  GSList **ret = rp;

  if(!cfg->root)
    *ret = g_slist_append(*ret, cfg);
}

struct config *ch_config_default() {
  GSList *c = NULL;
  struct config *ret = NULL;

  if(!cfg)
    if(ch_config_load(NULL))
      return NULL;
  g_slist_foreach(configs, match_root, &c);
  if(c)
    ret = c->data;
  g_slist_free(c);
  return ret;
}

int ch_config_values(struct config *c, char **currency, char **value, char **quota) {
  xmlNodePtr rem, tmp;
  if(!c || !c->condition)
    return -1;
  rem = xmlSecFindChild(c->condition, "remuneration", ns_charon);
  if(!rem)
    return -2;
  tmp = xmlSecFindChild(rem, "amount", ns_mcx);
  *currency = xmlGetProp(tmp, "currency");
  *value = xmlNodeGetContent(tmp);
  tmp = xmlSecFindChild(rem, "ratingQuantum", ns_charon);
  *quota = xmlNodeGetContent(tmp);
  return 0;
}
